package com.qall.util.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

import java.util.function.Function;

public class HttpServer implements Runnable {

    /**
     * 服务端口
     */
    private final int port;

    /**
     * 业务逻辑
     */
    private final Function<String, String> function;

    public HttpServer(Function function, int port) {
        this.function = function;
        this.port = port;
    }

    @Override
    public void run() {
        ServerBootstrap b = new ServerBootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();
        b.group(group)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch)
                            throws Exception {
                        System.out.println("initChannel ch:" + ch);
                        ch.pipeline()
                                // 1
                                .addLast("encoder", new HttpResponseEncoder())
                                .addLast("decoder", new HttpRequestDecoder()) // 2
                                .addLast("aggregator", new HttpObjectAggregator(512 * 1024))    // 3
                                .addLast("handler", new HttpHandler());        // 4
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128) // determining the number of connections queued
                .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);

        try {
            b.bind(port).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { // 1
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) {
            ByteBuf buf = msg.content();
            String data = buf.toString(StandardCharsets.UTF_8);
            String ret = function.apply(data);
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.wrappedBuffer(ret.getBytes())); // 2

            HttpHeaders heads = response.headers();
            heads.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + "; charset=UTF-8");
            heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 3
            heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);

            ctx.write(response);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            super.channelReadComplete(ctx);
            ctx.flush(); // 4
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("exceptionCaught");
            if (null != cause) cause.printStackTrace();
            if (null != ctx) ctx.close();
        }
    }


    public static void main(String[] args) {
        HttpServer httpServer = new HttpServer(x -> {
            System.out.println(x);
            return x;
        }, 8085);
        new Thread(httpServer).start();
    }

    void bioExample() {
        try {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(2181));
            while (true) {
                Socket socket = serverSocket.accept();
                if (socket != null) {
                    new Thread(() -> {
                     //handler socket
                    }).start();
                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
